home *** CD-ROM | disk | FTP | other *** search
- /**
- GRAB Graph Layout and Browser System
-
- Copyright (c) 1989, Tera Computer Company
- **/
-
- /**
- Implementation of the graph data structure.
- Just a list of representation of graphical objects.
- You can add new objects, draw the objects, erase the objects, delete
- the objects, change the values of the attributes of the objects.
- **/
-
- #include <InterViews/painter.h>
- #include <math.h>
- #include <InterViews/brush.h>
- #include <InterViews/defs.h>
- #include <InterViews/graphic.h>
- #include <InterViews/Graphic/lines.h>
- #include <InterViews/Graphic/polygons.h>
- #include <InterViews/Graphic/ellipses.h>
- #include "gview.h"
- #include "graph.h"
- #include "fonthandler.h"
- #include "tedge.h"
- #include "routines.h"
- #include "istring.h"
-
- const singlewidth = 1; /* width of a normal line */
- const boldwidth = 2; /* width of a bold line */
-
- Graph::Graph ()
- {
- /* initialize the brushes and colors */
- brushes[solidb] = single; /* normal line */
- brushes[bsolidb] = new Brush(0xffff, boldwidth); /* bold normal line */
- brushes[dottedb] = new Brush(0xcccc, singlewidth); /* dotted line */
- brushes[bdottedb] = new Brush(0xcccc, boldwidth); /* bold dotted line */
- brushes[dashedb] = new Brush(0xff00, singlewidth); /* dashed line */
- brushes[bdashedb] = new Brush(0xff00, boldwidth); /* bold dashed line */
-
- palette[blackc] = black;
- palette[grayc] = new Color("gray");
-
- if (!palette[grayc]->Valid())
- /**
- who knows what colors your machine knows about? It'd better know
- about black and white, though, or there'll be hell to pay.
-
- Of course, we could add more colors, but right now I don't see
- too many color monitors about, so you'll just have to be happy
- with these three. Most people don't have color plotters
- anyway, so printing color graphs could be ugly (postscript
- monochrome gray looks stupid enough as it is)
- **/
- {
- fprintf(stderr,
- "Color gray not supported. Using black for display.\n");
- palette[grayc] = black;
- }
-
- palette[whitec] = white;
-
- head = nil;
-
- /* initialize the canonical painters for nodes and edges */
- ndfault = new Painter();
- ndfault->FillBg(true);
- ndfault->SetColors(black, white);
- ndfault->SetPattern(solid);
- ndfault->SetBrush(brushes[solidb]);
-
- edfault = new Painter();
- edfault->FillBg(true);
- edfault->SetColors(black, white);
- edfault->SetPattern(solid);
- edfault->SetBrush(brushes[solidb]);
- }
-
- void Graph::SetFHandler(FontHandler* f)
- /* the graph has to know about the fonthandler */
- {
- fhandler = f;
- ndfault->SetFont(fhandler->node_font);
- edfault->SetFont(fhandler->edge_font);
- }
-
- void Graph::SetGB(GView* gview)
- /**
- the graph also likes to know about the graph view, so it can
- call routines there
- **/
- {
- gb = gview;
- }
-
- Graph::~Graph ()
- {
- register GraphData* p, * next;
-
- for (p = head; p != nil; p = next)
- {
- next = p->next;
-
- switch (p->gdtype)
- {
- case node:
- case dummy:
- delete (GNode *) p;
- break;
- case edge:
- case line:
- delete (GEdge *) p;
- break;
- }
- }
- }
-
- void Graph::DeleteAll ()
- /* this takes entirely too long and I don't know why */
- {
- register GraphData *p, *next;
-
- /* Does the garbage collector really work? Let's find out, cause
- deleting sure doesn't.
-
- for (p = head; p != nil; p = next)
- {
- next = p->next;
-
- switch (p->gdtype)
- {
- case node:
- case dummy:
- delete (GNode *) p;
- break;
- case edge:
- case line:
- delete (GEdge *) p;
- break;
- }
- }
- */
-
- head = nil;
- }
-
- /**
- for all the 'Add' routines, 'draw' indicates whether the object
- should be drawn immediately or not
- for nodes and edges, gp is a pointer to a component of the digraph
- structure, used along with ord to identify a node/edge. the only
- operation done on the gp for nodes is a comparison for equality.
- Edge gp's are actually pointers to tedges. The components of a tedge
- are accessed, but again only for comparisions
- **/
-
- void Graph::AddLine (Coord x1, Coord y1, Coord x2, Coord y2, BType linetype,
- CType color, boolean draw)
- /**
- add a line with no particular destination or source. Right now
- this is used only to show the levels of the graph.
- **/
- {
- Painter *p = new Painter (edfault);
-
- p->SetBrush(brushes[linetype]);
- p->SetColors(palette[color], white);
- MyGraphic* g = new MyGraphic(x1, y1, x2, y2, line, linetype, color, p);
-
- GEdge* e = new GEdge (g, linet, nil, false, "\0", false, nil, nil, -1);
- Append((GraphData*) e);
-
- if (draw)
- {
- DrawGD((GraphData*) e);
- }
- }
-
- void Graph::AddEdge (Coord x1, Coord y1, Coord x2, Coord y2, boolean arr,
- char* gp, char* s, BType linetype, CType color, boolean draw, int ord)
- /**
- Add an edge, maybe with arrows. gp corresponds to a tedge structure.
- s is the edge label.
- **/
- {
- boolean lab;
- MyGraphic* arr1 = nil;
- MyGraphic* arr2 = nil;
-
- //fprintf(stderr, "Ae: %d %d %d %d %d %d %s %d\n", x1, y1, x2, y2, arr, gp, s, linetype);
- Painter* p = new Painter(edfault);
- p->SetBrush(brushes[linetype]);
- p->SetColors(palette[color], white);
- p->SetFont(fhandler->edge_font);
- MyGraphic* g = new MyGraphic(x1, y1, x2, y2, line, linetype, color, p);
-
- lab = (fhandler->elabel_forced);
-
- if (arr)
- {
- MakeArrowHeads(x1, y1, x2, y2, &arr1, &arr2, linetype, color, p);
- }
-
- GEdge* e = new GEdge (g, edge, gp, lab, s, arr, arr1, arr2, ord);
- Append((GraphData *) e);
-
- if (draw)
- {
- DrawGD((GraphData*) e);
- }
- }
-
- const double DIST_FACTOR = 0.5; /* length of arrow lines, relatively */
- const double ARR_ANGLE = 30.0; /* angle between arrow lines and the edge */
- const double MAXDIST = 10.0; /* arrows get no bigger than this */
-
- void Graph::MakeArrowHeads(Coord x1, Coord y1, Coord x2, Coord y2,
- MyGraphic** arr1, MyGraphic** arr2, BType linetype, CType color,
- Painter* dfault)
- {
- double angle; /* angle between the line and the horizontal */
- double dist; /* length of one line of the arrow */
- double x_dist, y_dist;
- int a, b, c, d, e, f, g, height;
-
- gb->GetBounds(&a, &b, &c, &height, &d, &e, &f, &g);
-
- angle = atan2((double) (y1-y2), (double) (x1-x2));
- dist = (double) height * DIST_FACTOR * GetHeight();
- /* the arrow grows as the size of the canvas and the size of a node */
-
- if (dist > MAXDIST)
- {
- dist = MAXDIST;
- }
-
- x_dist = cos(angle) * dist;
- y_dist = sin(angle) * dist;
-
- *arr1 = new MyGraphic(x2, y2, x2 + (Coord) x_dist, y2 + (Coord) y_dist,
- line, linetype, color, new Painter(dfault));
- *arr2 = new MyGraphic(x2, y2, x2 + (Coord) x_dist, y2 + (Coord) y_dist,
- line, linetype, color, new Painter(dfault));
-
- (*arr1)->Rotate(ARR_ANGLE, x2, y2);
- (*arr2)->Rotate(-ARR_ANGLE, x2, y2);
- }
-
- void Graph::AddNode (Coord x1, Coord y1, Coord x2, Coord y2,
- NShape sh, char* s, boolean d, char* gp, char* upbc,
- char* downbc, BType linetype, CType color, boolean draw, int ord)
- /**
- Add a node. s is the node label; d indicates if it's a dummy node.
- gp is a pointer to the node in the digraph structure. Ord has meaning
- for dummy nodes: when drawing a dummy node that handles k edges,
- the node is split up into k points. ord indicates which one.
- **/
- {
- GNode* g;
- MyGraphic* gr;
- boolean lab;
-
- //fprintf(stderr, "An: %d %d %d %d %d %s %d %d %d %s %s\n", x1, y1, x2, y2, sh, s, d, gp, linetype, upbc, downbc);
- Painter* p = new Painter(ndfault);
- p->SetBrush(brushes[linetype]);
- p->SetColors(palette[color], white);
- p->SetFont(fhandler->node_font);
- gr = new MyGraphic(x1, y1, x2, y2, sh, linetype, color, p);
-
- lab = (fhandler->text_flag == true || fhandler->nlabel_forced == true);
-
- g = new GNode (gr, d ? dummy : node, gp, lab, s, upbc, downbc, ord);
- Append((GraphData *) g);
-
- if (draw)
- {
- DrawGD((GraphData*) g);
- }
- }
-
- void Graph::ChangeNodeLabel(char* node, char* s)
- /* change node's label to s */
- {
- GNode *gn = FindNode(node);
-
- if (gn->text)
- {
- EraseLabel(gn);
- EraseGraphic(gn); /* labels can overlap the node, so redraw it */
- DrawGraphic(gn);
- }
-
- gn->lstr = strdup(s);
-
- if (fhandler->text_flag || fhandler->nlabel_forced)
- /* only draw the label if it fits or node labels are forced */
- {
- gn->gr->GetPainter()->SetFont(fhandler->node_font);
- DrawLabel(gn);
- gn->text = true;
- }
- else
- {
- gn->text = false;
- }
- }
-
- void Graph::ChangeEdgeLabel(char* fromnode, char* tonode, int ord, char* s)
- /**
- change the label of the edge from fromnode to tonode of ordinality ord
- to s.
- **/
- {
- GEdge *ge = FindEdge(fromnode, tonode, ord);
-
- if (ge->text)
- {
- EraseLabel(ge);
- EraseGraphic(ge);
- DrawGraphic(ge); /* labels can overlap the edge, so redraw it */
- }
-
- ge->lstr = strdup(s);
-
- if (fhandler->elabel_forced)
- /* draw only if edge labels are forced */
- {
- ge->gr->GetPainter()->SetFont(fhandler->edge_font);
- DrawLabel(ge);
- ge->text = true;
- }
- else
- {
- ge->text = false;
- }
- }
-
- void Graph::ChangeBC()
- /* the bc flag has been changed. Either draw or erase them all */
- {
- GNode* gn;
- GraphData* p;
-
- for (p = head; p != nil; p = p->next)
- {
- if (p->gdtype == node || p->gdtype == dummy)
- {
- gn = (GNode *) p;
-
- if (GetBCFlag())
- {
- DrawBC(gn);
- }
- else
- {
- EraseBC(gn);
- EraseGraphic(gn); /* bc's overlap the node, so redraw it */
- DrawGraphic(gn);
- }
- }
- }
- }
-
- void Graph::ChangeMarkDummy()
- /* the mark dummy flag has been changed. */
- {
- GNode* gn;
- GraphData* p;
- Painter *pa;
- MyGraphic *g;
- BType brush;
- CType color;
-
- Coord x0, y0, x1, y1;
-
- for (p = head; p != nil; p = p->next)
- {
- if (p->gdtype == dummy)
- {
- gn = (GNode *) p;
-
- pa = new Painter(gn->gr->GetPainter());
- brush = gn->gr->GetBrush();
- color = gn->gr->GetColor();
- gn->gr->GetOriginal(x0, y0, x1, y1);
- delete gn->grf;
-
- if (GetMarkDummyFlag())
- /* change dummy node so it's a teeny rectangle */
- {
- g = new MyGraphic(x0 - 1, y0 - 1, 2, 2, rectangle, brush,
- color, pa);
- gn->grf = new FillRect(x0 - 1, y0 - 1, x0 + 1, y0 + 1);
- EraseGraphic(gn);
- delete gn->gr;
- gn->gr = g;
- DrawGraphic(gn);
- }
- else
- /* change it back to a point */
- {
- g = new MyGraphic(x0 + 1, y0 + 1, x0 + 1, y0 + 1, npoint,
- brush, color, pa);
- gn->grf = new Point(x0 + 1, y0 + 1);
- EraseGraphic(gn);
- delete gn->gr;
- gn->gr = g;
- DrawGraphic(gn);
- }
- }
- }
- }
-
- /**
- the ChangeLabels routines are called when labels change size, or
- suddenly become displayed, or suddenly become undisplayed.
- **/
- void Graph::ChangeLabels()
- {
- ChangeAllLabels(true, true);
- }
-
- void Graph::ChangeForceNL()
- {
- ChangeAllLabels(true, false);
- }
-
- void Graph::ChangeForceEL()
- {
- ChangeAllLabels(false, true);
- }
-
- void Graph::ChangeAllLabels(boolean doNode, boolean doEdge)
- /**
- if doNode is true, change node labels
- if doEdge is true, change edge labels
- **/
- {
- GNode* gn;
- GEdge* ge;
- GraphData* p;
-
- for (p = head; p != nil; p = p->next)
- {
- if (doNode && (p->gdtype == node || p->gdtype == dummy))
- {
- gn = (GNode *) p;
-
- if (gn->text)
- {
- EraseLabel(gn);
- EraseGraphic(gn);
- DrawGraphic(gn); /* labels can overlap the node, so redraw it */
- }
-
- if (fhandler->text_flag == true || fhandler->nlabel_forced == true)
- /* draw node labels if they fit or they're forced */
- {
- gn->text = true;
- gn->gr->GetPainter()->SetFont(fhandler->node_font);
- DrawLabel(gn);
- }
- else
- {
- gn->text = false;
- }
- }
- else if (doEdge && p->gdtype == edge)
- {
- ge = (GEdge *) p;
-
- if (ge->text)
- {
- EraseLabel(ge);
- EraseGraphic(ge);
- DrawGraphic(ge); /* labels can overlap the edge, so redraw it */
- }
-
- if (fhandler->elabel_forced)
- /* draw edge labels if they are forced */
- {
- ge->text = true;
- ge->gr->GetPainter()->SetFont(fhandler->edge_font);
- DrawLabel(ge);
- }
- else
- {
- ge->text = false;
- }
- }
- }
- }
-
- void Graph::ChangePA()
- /* the print arrows option has changed */
- {
- GEdge* ge;
- GraphData* p;
-
- for (p = head; p != nil; p = p->next)
- {
- if (p->gdtype == edge)
- {
- ge = (GEdge *) p;
-
- if (ge->arrow && GetPAFlag())
- /**
- Draw arrows for this edge. Arrowheads are only created
- if they're displayed. This saves time when displaying
- the graph. But that means we have to create them here.
- **/
- {
- int x1, y1, x2, y2;
- MyGraphic* arr1;
- MyGraphic* arr2;
- BType linetype = ge->gr->GetBrush();
- CType color = ge->gr->GetColor();
-
- Painter* p = new Painter(edfault);
- p->SetBrush(brushes[linetype]);
- p->SetColors(palette[color], white);
- p->SetFont(fhandler->edge_font);
-
- ge->gr->GetOriginal(x1, y1, x2, y2);
- MakeArrowHeads(x1, y1, x2, y2, &arr1, &arr2, linetype, color,
- p);
- ge->arr1 = arr1;
- ge->arr2 = arr2;
- DrawArrows(ge);
- }
- else if (ge->arrow)
- {
- EraseArrows(ge);
- delete ge->arr1;
- delete ge->arr2;
- ge->arr1 = nil;
- ge->arr2 = nil;
- }
- }
- }
- }
-
- GraphData::GraphData ()
- /* used at one point to allocate an array of them */
- {
- }
-
- GraphData::GraphData (GDType t, char* gp)
- /**
- used in myGraphicsIntersecting, when we're allocating dummy GraphData
- objects
- **/
- {
- gr = nil;
- gdtype = t;
- gpart = gp;
- text = false;
- lstr = "\0";
- next = nil;
- ord = -1;
- }
-
- GraphData::GraphData (MyGraphic* g, GDType t, char* gp, boolean te, char* str,
- int o)
- /**
- The standard constructor routine for real nodes and edges
- **/
- {
- gr = g;
- gdtype = t;
- gpart = gp;
- text = te;
-
- if (strcmp(str, "\0"))
- {
- lstr = strdup(str);
- }
- else
- {
- lstr = "\0";
- }
-
- ord = o;
- next = nil;
- }
-
- GraphData::~GraphData()
- {
- delete gr;
- delete lstr;
- }
-
- /**
- In the following 4 routines, the any flag is sometimes used to
- denote that you can ignore the ord of a node/edge when searching
- for it. The ord routine doesn't enter into FindNode.
- Since the ord is always defined for all edges, it isn't necessary for
- FindEdge. In RemoveNode and RemoveEdge, the any flag is used to
- delete all edges: Call RemoveNode/Edge with the any flag set until
- all the nodes/edges that match are deleted. This avoids having to know
- how many nodes/edges there are that fit the criteria.
- **/
-
- GNode *Graph::RemoveNode (char* n, int ord, boolean any)
- /**
- find the node whose gpart is the same as n and ord the same as ord.
- Remove it from the graph and return it.
- **/
- {
- GNode *rval = nil;
- boolean found = false;
-
- if (head == nil)
- /**
- yes, this can happen. See DelAllNodes. Useful sanity check,
- anyway.
- **/
- {
- }
- else if ((head->gdtype == node || head->gdtype == dummy) &&
- (head->gpart == n) && (any || head->ord == ord))
- /* the head matches */
- {
- rval = (GNode *) head;
- head = head->next;
- }
- else /* check the rest of the list */
- {
- for (GraphData* p = head; !found; p = p->next)
- {
- if (p->next == nil)
- /* no match */
- {
- break;
- }
- else if ((p->next->gdtype == node || p->next->gdtype == dummy) &&
- (p->next->gpart == n) &&
- (any || p->next->ord == ord))
- {
- found = true;
- rval = (GNode *) p->next;
- p->next = p->next->next;
- }
- }
- }
-
- return rval;
- }
-
- GNode* Graph::FindNode (char* n)
- /**
- find the first node whose gpart is the same as n and ord the same as ord.
- **/
- {
- GNode *rval = nil;
- boolean found = false;
-
- if ((head->gdtype == node || head->gdtype == dummy) &&
- (head->gpart == n))
- /* head matches */
- {
- rval = (GNode *) head;
- }
- else /* check the rest of the list */
- {
- for (GraphData* p = head; found == false; p = p->next)
- {
- if (p->next == nil)
- {
- fprintf (stderr, "Find Node: not found\n");
- break;
- }
- else if ((p->next->gdtype == node || p->next->gdtype == dummy) &&
- (p->next->gpart == n))
- {
- found = true;
- rval = (GNode *) p->next;
- }
- }
- }
-
- return rval;
- }
-
- GEdge *Graph::RemoveEdge (char* prev, char* next, int ord, boolean any)
- /**
- find the edge whose gpart has a fromnode to match prev, and a tonode
- to match next, and an ord to match ord. Remove it from the graph and
- return it.
- **/
- {
- GEdge *rval = nil;
- boolean found = false;
-
- if (head == nil)
- /**
- I doubt this could happen, but a useful sanity check,
- anyway.
- **/
- {
- }
- else if ((head->gdtype == edge) &&
- (((TEDGE *) head->gpart)->fromnode == prev) &&
- (((TEDGE *) head->gpart)->tonode == next) &&
- (any || head->ord == ord))
- /* head matches */
- {
- rval = (GEdge *) head;
- head = head->next;
- }
- else /* check the rest of the list */
- {
- for (GraphData* p = head; found == false; p = p->next)
- {
- if (p->next == nil)
- /* no match */
- {
- break;
- }
- else if ((p->next->gdtype == edge) &&
- (((TEDGE *) p->next->gpart)->fromnode == prev) &&
- (((TEDGE *) p->next->gpart)->tonode == next) &&
- (any || p->next->ord == ord))
- {
- found = true;
- rval = (GEdge *) p->next;
- p->next = p->next->next;
- }
- }
- }
-
- return rval;
- }
-
- GEdge *Graph::FindEdge (char* prev, char* next, int ord)
- /**
- find the edge whose gpart has a fromnode to match prev, and a tonode
- to match next, and whose ord matches ord. return it.
- **/
- {
- GEdge *rval = nil;
- boolean found = false;
-
- if ((head->gdtype == edge) &&
- (((TEDGE *) head->gpart)->fromnode == prev) &&
- (((TEDGE *) head->gpart)->tonode == next) &&
- head->ord == ord)
- /* head matches */
- {
- rval = (GEdge *) head;
- }
- else /* check the rest of the list */
- {
- for (GraphData* p = head; found == false; p = p->next)
- {
- if (p->next == nil)
- {
- fprintf (stderr, "Find Edge: not found\n");
- break;
- }
- else if ((p->next->gdtype == edge) &&
- (((TEDGE *) p->next->gpart)->fromnode == prev) &&
- (((TEDGE *) p->next->gpart)->tonode == next) &&
- p->next->ord == ord)
- {
- found = true;
- rval = (GEdge *) p->next;
- }
- }
- }
-
- return rval;
- }
-
- GNode::GNode (MyGraphic* g, GDType t, char* gp, boolean lab, char* str,
- char* upbc, char* downbc, int ord)
- : (g, t, gp, lab, str, ord)
- {
- int x0, y0, x1, y1;
-
- g->GetOriginal(x0, y0, x1, y1);
- grf = makeFill(g->GetShape(), x0, y0, x1, y1);
- upstr = strdup(upbc);
- downstr = strdup(downbc);
- }
-
- Graphic* GNode::makeFill(NShape s, Coord x0, Coord y0, Coord x1, Coord y1)
- /**
- The nodes all keep a filled graphic object around. This way, we
- can easily test if the cursor is over a node by seeing if it intersects
- the graphic.
- **/
- {
- switch (s)
- {
- case circle:
- return new FillCircle(x0, y0, y1);
-
- case oval:
- return new FillEllipse(x0, y0, x1, y1);
-
- case npoint:
- return new Point(x0, y0);
-
- case diamond:
- {
- Coord x[4], y[4];
-
- x[0] = x0 - x1; y[0] = y0;
- x[1] = x0; y[1] = y0 - y1;
- x[2] = x0 + x1; y[2] = y0;
- x[3] = x0; y[3] = y0 + y1;
- return new FillPolygon(x, y, 4);
- }
-
- case double_box:
- case rectangle:
- return new FillRect(x0 - x1, y0 - y1, x0 + x1, y0 + y1);
-
- case line:
- /* better not be a line! */
- return nil;
- }
- }
-
- GEdge::GEdge (MyGraphic* g, GDType t, char* gp, boolean lab, char* s,
- boolean arr, MyGraphic* a1, MyGraphic* a2, int ord)
- : (g, t, gp, lab, s, ord)
- {
- arrow = arr;
- arr1 = a1;
- arr2 = a2;
- }
-
- int Graph::myGraphicsIntersecting(BoxObj& b, GraphData**& garray)
- /**
- so-called because it resembles the GraphicsIntersecting routine
- of the InterViews class GraphicBlock. Return the objects which
- intersect the box in the garray. Don't bother testing line objects.
- Node/dummy node intersecting is done with InterViews routines.
- The InterViews line intersection routine was bogus last time I looked,
- so use the routine in intersect.c
- **/
- {
- GraphData* p, *q, *gdlist;
- Graphic* ng;
- MyGraphic* eg;
- int size = 0, n = 0;
- Coord x0, y0, x1, y1;
-
- gdlist = nil;
-
- /* create a list of objects that intersect */
- for (p = head; p != nil; p = p->next)
- {
- switch (p->gdtype)
- {
- case node:
- case dummy:
- /* see if the FILLED graphic intersects it */
- ng = (((GNode*)p)->grf);
-
- if (ng->Intersects(b))
- {
- q = new GraphData(p->gdtype, p->gpart);
- q->next = gdlist;
- gdlist = q;
- ++size;
- }
- break;
- case edge:
- eg = p->gr;
- eg->GetOriginal(x0, y0, x1, y1);
-
- if (Intersect(x0, y0, x1, y1, b.left, b.bottom, b.right, b.top))
- {
- q = new GraphData(p->gdtype, p->gpart);
- q->next = gdlist;
- gdlist = q;
- ++size;
- }
- break;
- case line:
- break;
- }
- }
-
- /* change the list into an array */
- garray = new GraphData*[size];
-
- for (p = gdlist; p != nil; ++n)
- {
- garray[n] = p;
- p = p->next;
- }
-
- return size;
- }
-
- void Graph::Append(GraphData *g)
- /* append this to the graph */
- {
- g->next = head;
- head = g;
- }
-
- void Graph::DelAllNodes(char* node)
- /**
- delete all nodes with a gpart that matches node. This is useful
- for dummy nodes, which are actually a series of subnodes in the
- graph data structure (each subnode has the same gpart, but a different
- ord
- **/
- {
- GNode *gn;
- int dummy;
-
- while ((gn = RemoveNode(node, dummy, true)) != nil)
- {
- DelNode(gn);
- }
- }
-
- void Graph::DelNode(char* node, int ord)
- /* Delete a node, we know its ord */
-
- {
- GNode *gn = RemoveNode(node, ord);
- DelNode(gn);
- }
-
- void Graph::DelAllEdges(char* prev, char* next)
- /**
- delete all edges from prev to next, regardless of the ord.
- Again, useful when deleting dummy nodes, but in other cases, too
- **/
- {
- GEdge *ge;
- int dummy;
-
- while ((ge = RemoveEdge(prev, next, dummy, true)) != nil)
- {
- DelEdge(ge);
- }
- }
-
- void Graph::DelEdge(char* prev, char* next, int ord)
- /* delete a specific edge */
- {
- GEdge *ge = RemoveEdge(prev, next, ord);
- DelEdge(ge);
- }
-
- void Graph::DelNode(GNode *gn)
- /* delete this gnode. Erase the text, bc, and the node itself */
- {
- if (gn->text)
- {
- EraseLabel(gn);
- }
-
- if (GetBCFlag())
- {
- EraseBC(gn);
- }
-
- EraseGraphic(gn);
- }
-
- void Graph::DelEdge(GEdge *ge)
- /* erase the label, the arrows, and the line itself */
- {
- if (ge == nil)
- {
- fprintf (stderr, "Delete Edge: nothing to delete!");
- return;
- }
-
- if (ge->text)
- {
- EraseLabel(ge);
- }
-
- if (ge->arr1 != nil)
- {
- EraseArrows(ge);
- }
-
- EraseGraphic(ge);
- }
-
- void Graph::ChangeEdge(char* prev, char* next, int ord, BType brush,
- CType color)
- /* change the brush and color of the matching edge */
- {
- GEdge *ge = FindEdge(prev, next, ord);
-
- if (ge != nil)
- {
- BType b = ge->gr->GetBrush();
- CType c = ge->gr->GetColor();
-
- if (b != brush || c != color)
- {
- if (ge->text)
- {
- EraseLabel(ge);
- }
-
- EraseGraphic(ge);
- ge->gr->GetPainter()->SetBrush(brushes[brush]);
- ge->gr->GetPainter()->SetColors(palette[color], white);
- DrawGraphic(ge);
-
- if (ge->text)
- {
- DrawLabel(ge);
- }
-
- if (ge->arrow && GetPAFlag())
- /* change the arrows, too */
- {
- EraseArrows(ge);
- ge->arr1->GetPainter()->SetBrush(brushes[brush]);
- ge->arr2->GetPainter()->SetBrush(brushes[brush]);
- ge->arr1->GetPainter()->SetColors(palette[color], white);
- ge->arr2->GetPainter()->SetColors(palette[color], white);
- DrawArrows(ge);
- ge->arr1->SetBrush(brush);
- ge->arr1->SetColor(color);
- ge->arr2->SetBrush(brush);
- ge->arr2->SetColor(color);
- }
-
- ge->gr->SetBrush(brush);
- ge->gr->SetColor(color);
- }
- }
- }
-
- void Graph::ChangeNode(char* node, NShape shape, BType brush, CType color)
- /* change the shape, brush, and color of the matching node */
- {
- GNode *gn = FindNode(node);
-
- if (gn != nil)
- {
- NShape s = gn->gr->GetShape();
- BType b = gn->gr->GetBrush();
- CType c = gn->gr->GetColor();
-
- if (s != shape)
- /* must create a new graphic and fill graphic */
- {
- Coord x1, y1, x2, y2;
-
- gn->gr->GetOriginal(x1, y1, x2, y2);
-
- if (gn->text)
- {
- EraseLabel(gn);
- }
-
- EraseGraphic(gn);
-
- Painter* p = new Painter(ndfault);
- p->SetBrush(brushes[brush]);
- p->SetColors(palette[color], white);
- p->SetFont(fhandler->node_font);
-
- MyGraphic* g = new MyGraphic(x1, y1, x2, y2, shape, brush, color,
- p);
- Graphic* gf = gn->makeFill(shape, x1, y1, x2, y2);
- delete gn->gr;
- delete gn->grf;
- gn->gr = g;
- gn->grf = gf;
- DrawGraphic(gn);
-
- if (gn->text)
- {
- DrawLabel(gn);
- }
- }
- else if (b != brush || c != color)
- /* less drastic change */
- {
- if (gn->text)
- {
- EraseLabel(gn);
- }
-
- EraseGraphic(gn);
- gn->gr->GetPainter()->SetBrush(brushes[brush]);
- gn->gr->GetPainter()->SetColors(palette[color], white);
- gn->gr->SetBrush(brush);
- gn->gr->SetColor(color);
- DrawGraphic(gn);
-
- if (gn->text)
- {
- DrawLabel(gn);
- }
- }
- }
- }
-
- GNode::~GNode ()
- {
- delete gr;
- delete lstr;
- delete grf;
- delete upstr;
- delete downstr;
- }
-
- GEdge::~GEdge ()
- {
- delete gr;
- delete lstr;
- delete arr1;
- delete arr2;
- }
-
- void Graph::SetupMove(char* mnode, char* prev[], char* next[], int numprev,
- int numnext, Coord* wid, Coord* ht)
- /**
- We're about to move node mnode. prev and next are a list of its
- predecessors and successors, with numprev and numnext to indicate
- how big they are. Return the half-width and half-height of the node in
- wid and ht.
-
- Remove the node and all associated edges
- **/
- {
- GNode* gn = FindNode(mnode);
-
- *wid = gn->gr->Width() / 2;
- *ht = gn->gr->Height() / 2;
-
- DelAllNodes(mnode);
-
- for (int i = 0; i < numprev; i++)
- {
- DelAllEdges(prev[i], mnode);
- }
-
- for (i = 0; i < numnext; i++)
- {
- DelAllEdges(mnode, next[i]);
- }
- }
-
- static const double RADPERDEG = PI/180.0;
-
- void MyGraphic::Rotate(double angle, Coord x = 0, Coord y = 0)
- /**
- Rotate a graphic by the angle (in degrees), around the point (x, y)
- Formula lifted from the PostScript reference manual, if you can believe
- it.
- **/
- {
- double tx0, ty0, tx1, ty1, tmp1, tmp2;
- angle *= RADPERDEG;
- tmp1 = cos(angle);
- tmp2 = sin(angle);
- tx0 = (double) x0 - x;
- ty0 = (double) y0 - y;
- tx1 = (double) x1 - x;
- ty1 = (double) y1 - y;
-
- tx0 = tmp1*tx0 - tmp2*ty0;
- ty0 = tmp2*tx0 + tmp1*ty0;
- tx1 = tmp1*tx1 - tmp2*ty1;
- ty1 = tmp2*tx1 + tmp1*ty1;
-
- x0 = round(tx0) + x;
- y0 = round(ty0) + y;
- x1 = round(tx1) + x;
- y1 = round(ty1) + y;
- }
-
- void MyGraphic::Translate(Coord dx, Coord dy)
- /* translate a graphic by dx and dy */
- {
- x0 += dx;
- x1 += dx;
- y0 += dy;
- y1 += dy;
- }
-
- void MyGraphic::Scale(float sx, float sy)
- /* scale a graphic by sx and sy */
- {
- x0 = round(x0 * sx);
- x1 = round(x1 * sx);
- y0 = round(y0 * sy);
- y1 = round(y1 * sy);
- }
-
- MyGraphic::MyGraphic(Coord x, Coord y, Coord xx, Coord yy, NShape s,
- BType b, CType c, Painter* p)
- {
- x0 = x;
- y0 = y;
- x1 = xx;
- y1 = yy;
- shape = s;
- painter = p;
- brush = b;
- color = c;
- }
-
- MyGraphic::~MyGraphic()
- {
- delete painter;
- }
-
- /* erase by changing the foreground color to white and drawing */
-
- void Graph::EraseLabel(GNode* gn)
- {
- Color *c = gn->gr->GetPainter()->GetFgColor();
-
- gn->gr->GetPainter()->SetColors(white, white);
- DrawLabel(gn);
- gn->gr->GetPainter()->SetColors(c, white);
- }
-
- void Graph::EraseLabel(GEdge* ge)
- {
- Color *c = ge->gr->GetPainter()->GetFgColor();
-
- ge->gr->GetPainter()->SetColors(white, white);
- DrawLabel(ge);
- ge->gr->GetPainter()->SetColors(c, white);
- }
-
- void Graph::EraseBC(GNode* gn)
- {
- Color *c = gn->gr->GetPainter()->GetFgColor();
-
- gn->gr->GetPainter()->SetColors(white, white);
- DrawBC(gn);
- gn->gr->GetPainter()->SetColors(c, white);
- }
-
- void Graph::EraseArrows(GEdge* ge)
- {
- Color *c = ge->gr->GetPainter()->GetFgColor();
-
- ge->arr1->GetPainter()->SetColors(white, white);
- ge->arr2->GetPainter()->SetColors(white, white);
- DrawArrows(ge);
- ge->arr1->GetPainter()->SetColors(c, white);
- ge->arr2->GetPainter()->SetColors(c, white);
- }
-
- void Graph::EraseGraphic(GraphData* g)
- {
- Color *c = g->gr->GetPainter()->GetFgColor();
-
- //
- Painter *p = g->gr->GetPainter();
- g->gr->SetPainter(new Painter(p));
- g->gr->GetPainter()->SetColors(white, white);
- DrawGraphic(g);
- g->gr->SetPainter(p);
- // g->gr->GetPainter()->SetColors(c, white);
- }
-
- void Graph::DrawLabel(GNode* gn)
- /* draw the node label (in the center of the node */
- {
- Canvas* c = gb->GetCanvas();
-
- int width = gn->gr->GetPainter()->GetFont()->Width(gn->lstr,
- strlen(gn->lstr));
- int height = gn->gr->GetPainter()->GetFont()->Height();
- int cx = gn->gr->XCenter();
- int cy = gn->gr->YCenter();
-
- gn->gr->GetPainter()->Text(c, gn->lstr, cx - width / 2, cy - height / 2);
- }
-
- void Graph::DrawLabel(GEdge* ge)
- /* draw an edge label (to the right of the edge, about in the center) */
- {
- Coord x1, y1, x2, y2;
- Canvas* c = gb->GetCanvas();
-
- int width = ge->gr->GetPainter()->GetFont()->Width(ge->lstr,
- strlen(ge->lstr));
- int height = ge->gr->GetPainter()->GetFont()->Height();
- ge->gr->GetOriginal(x1, y1, x2, y2);
-
- Coord xpos = (x1 + x2) / 2;
- Coord ypos = (y1 + y2) / 2;
-
- /**
- multigraph support: labels for edges should be staggered so
- they don't overrun each other
- **/
-
- if ((y1 - y2) / 2 != 0)
- /* don't bother to change labels for horizontal edges */
- {
- Coord shift = (height * (ge->ord / 2)) % abs((y1 - y2) / 2) *
- ((ge->ord % 2 == 0) ? -1 : 1);
- /**
- Got that? Same formula as in clip.h, except we're shifting
- upward or downward by the height of the font. Also, take
- the remainder with respect to half the length of the edge so
- we don't go too far
- **/
-
- ypos += shift;
- xpos += shift * (x1 - x2) / (y1 - y2);
- }
-
- ge->gr->GetPainter()->Text(c, ge->lstr, xpos, ypos);
- }
-
- void Graph::DrawBC(GNode* gn)
- /**
- Up barycenters go at the top of the node, centered.
- Down barycenters go at the bottom of the node, centered
- **/
- {
- Canvas* c = gb->GetCanvas();
-
- int uwidth = gn->gr->GetPainter()->GetFont()->Width(gn->upstr,
- strlen(gn->upstr));
- int dwidth = gn->gr->GetPainter()->GetFont()->Width(gn->downstr,
- strlen(gn->downstr));
- int height = gn->gr->GetPainter()->GetFont()->Height();
- int cx = gn->gr->XCenter();
- int by = gn->gr->YBottom();
- int ty = gn->gr->YTop();
-
- gn->gr->GetPainter()->Text(c, gn->downstr, cx - dwidth / 2,
- by - height / 2);
- gn->gr->GetPainter()->Text(c, gn->upstr, cx - uwidth / 2, ty - height / 2);
- }
-
- void Graph::DrawArrows(GEdge* ge)
- /* pretty straightforward */
- {
- Coord x1, y1, x2, y2;
- Canvas* c = gb->GetCanvas();
-
- ge->arr1->GetOriginal(x1, y1, x2, y2);
- ge->arr1->GetPainter()->Line(c, x1, y1, x2, y2);
- ge->arr2->GetOriginal(x1, y1, x2, y2);
- ge->arr2->GetPainter()->Line(c, x1, y1, x2, y2);
- }
-
- void Graph::DrawGraphic(GraphData* g)
- /**
- except for the line, the x1 and y1 coordinates work as width and height,
- x0 and y0 work as the center
- **/
- {
- Coord x0, y0, x1, y1;
- Canvas* c = gb->GetCanvas();
-
- g->gr->GetOriginal(x0, y0, x1, y1);
-
- switch(g->gr->GetShape())
- {
- case circle:
- /* use the height, not the width, as the radius */
- g->gr->GetPainter()->Circle(c, x0, y0, y1);
- break;
- case oval:
- g->gr->GetPainter()->Ellipse(c, x0, y0, x1, y1);
- break;
- case npoint:
- g->gr->GetPainter()->Point(c, x0, y0);
- break;
- case diamond:
- {
- Coord x[4], y[4];
-
- x[0] = x0 - x1; y[0] = y0;
- x[1] = x0; y[1] = y0 - y1;
- x[2] = x0 + x1; y[2] = y0;
- x[3] = x0; y[3] = y0 + y1;
- g->gr->GetPainter()->Polygon(c, x, y, 4);
- }
- break;
- case double_box:
- g->gr->GetPainter()->Rect(c, x0 - x1, y0 - y1, x0 + x1, y0 + y1);
- /**
- mins and maxs are to insure the lines don't go past the
- center (which they can if the box is tiny enough). For
- example, if the the graph is too short, the top of the
- inner box could be below the bottom, which looks strange.
- **/
- g->gr->GetPainter()->Rect(c, min(x0 - x1 + db_margin, x0),
- min(y0 - y1 + db_margin, y0),
- max(x0 + x1 - db_margin, x0),
- max(y0 + y1 - db_margin, y0));
- break;
- case rectangle:
- g->gr->GetPainter()->Rect(c, x0 - x1, y0 - y1, x0 + x1, y0 + y1);
- break;
- case line:
- g->gr->GetPainter()->Line(c, x0, y0, x1, y1);
- break;
- }
- }
-
- int MyGraphic::Width()
- {
- switch(shape)
- {
- case circle:
- /* use height as the radius */
- return y1 * 2;
- case oval:
- case diamond:
- case double_box:
- case rectangle:
- return x1 * 2;
- case npoint:
- return 0;
- case line:
- return x1 - x0;
- }
- }
-
- int MyGraphic::Height()
- {
- switch(shape)
- {
- case circle:
- return y1 * 2;
- case oval:
- case diamond:
- case double_box:
- case rectangle:
- return y1 * 2;
- case npoint:
- return 0;
- case line:
- return y1 - y0;
- }
- }
-
- Coord MyGraphic::XCenter()
- {
- switch(shape)
- {
- case circle:
- case oval:
- case npoint:
- case diamond:
- case double_box:
- case rectangle:
- return x0;
- case line:
- return (x0 + x1) / 2;
- }
- }
-
- Coord MyGraphic::YCenter()
- {
- switch(shape)
- {
- case circle:
- case oval:
- case npoint:
- case diamond:
- case double_box:
- case rectangle:
- return y0;
- case line:
- return (y0 + y1) / 2;
- }
- }
-
- Coord MyGraphic::YBottom()
- {
- switch(shape)
- {
- case circle:
- return y0 - y1;
- case oval:
- case diamond:
- case double_box:
- case rectangle:
- return y0 - y1;
- case npoint:
- case line:
- return y0;
- }
- }
-
- Coord MyGraphic::YTop()
- {
- switch(shape)
- {
- case circle:
- return y0 + y1;
- case oval:
- case diamond:
- case double_box:
- case rectangle:
- return y0 + y1;
- case npoint:
- case line:
- return y0;
- }
- }
-
- void Graph::DrawAll()
- /* draw everything */
- {
- GraphData* p;
-
- for (p = head; p != nil; p = p->next)
- {
- DrawGD(p);
- }
- }
-
- void Graph::DrawGD(GraphData* g)
- /* draw one thing */
- {
- GNode* gn;
- GEdge* ge;
-
- DrawGraphic(g);
-
- if ((g->gdtype == node || g->gdtype == dummy))
- {
- gn = (GNode *) g;
-
- if (gn->text)
- {
- DrawLabel(gn);
- }
-
- if (GetBCFlag())
- {
- DrawBC(gn);
- }
- }
- else if (g->gdtype == edge || g->gdtype == line)
- {
- ge = (GEdge *) g;
-
- if (ge->text)
- {
- DrawLabel(ge);
- }
-
- if (ge->arrow && GetPAFlag())
- {
- DrawArrows(ge);
- }
- }
- }
-